Opencv图像识别从零到精通

您所在的位置:网站首页 opencv map函数 Opencv图像识别从零到精通

Opencv图像识别从零到精通

2022-03-24 11:50| 来源: 网络整理| 查看: 265

安装教程与调试显示成功

       不得不说,opencv的安装需要很久,也会出现很多的错误,也是参考了很多的安装教程,综合了好几个全面可靠,最后显示图像的时候,还是很开心的。先来一个调试成功,图像显示的界面。

        想学opencv的应该都有c++基础,至于怎么在vs2010中建立工程,这里应该就不用多说了,不会的可以百度一下 vs2010怎么建立工程。

        这里提供一篇文章参考建立程http://jingyan.baidu.com/article/5552ef473d44f5518ffbc9fd.html

测试代码如下:#include using namespacestd;using namespacecv;int main(intargc, char* argv[]){    const char* imagename = "lena.jpg";    //从文件中读入图像    Mat img = imread(imagename);    //如果读入图像失败    if (img.empty())    {        fprintf(stderr, "Can not load image %s\n", imagename);        return -1;    }    //显示图像    imshow("image", img);    //此函数等待按键,按键盘任意键就返回    waitKey();    return 0;}       注意:唯一你要注意的是对于这个代码你要显示的图片要放对位置,看下图的位置,相应打开你的文件夹,至于为什么会在后面的图像显示教程中显示将函数和路径,这里只要安装好,能显示就可以了。(想多了也没用)

一、解压        下载完后得到文件,解压到D:\Program Files二、.配置环境变量

配置方法:右击我的电脑——属性——高级——环境变量——系统变量  

变量名为PATH  变量值为D:\Program Files\opencv\build\x64\vc10\bin(我是64位+vs2010,如果你的是32位,选x86)

三.配置工程(很重要)

建立好一个工程,不会的上面有连接vs2010建立工程,然后菜单栏中找到属性管理器,点击项目->Debug|Win32->Microsoft.Cpp.Win32.userDirectories(右键属性,或者双击)即可打开属性页面----通用属性 ---VC++目录 ---包含目录中添加上

D:\Program Files\opencv\build\includeD:\Program Files\opencv\build\include\opencvD:\Program Files\opencv\build\include\opencv2 这三个目录。

在库目录中,添加上D:\Program Files\opencv\build\x86\vc10\lib  (这个路径,无论是32位还是64位电脑都选x86)

通用属性-- -链接器----输入->---附加的依赖项

opencv_ml248d.libopencv_calib3d248d.libopencv_contrib248d.libopencv_core248d.libopencv_features2d248d.libopencv_flann248d.libopencv_gpu248d.libopencv_highgui248d.libopencv_imgproc248d.libopencv_legacy248d.libopencv_objdetect248d.libopencv_ts248d.libopencv_video248d.libopencv_nonfree248d.libopencv_ocl248d.libopencv_photo248d.libopencv_stitching248d.libopencv_superres248d.libopencv_videostab248d.lib

opencv_objdetect248.libopencv_ts248.libopencv_video248.libopencv_nonfree248.libopencv_ocl248.libopencv_photo248.libopencv_stitching248.libopencv_superres248.libopencv_videostab248.libopencv_calib3d248.libopencv_contrib248.libopencv_core248.libopencv_features2d248.libopencv_flann248.libopencv_gpu248.libopencv_highgui248.libopencv_imgproc248.libopencv_legacy248.lib

opencv_ml248.lib

OK,这样你可以看到最开始的那个图了。如果出现图像显示问题,不是配置问题的话么可以看第三节的图像显示,会具体的给出路径了,和图像显示长出现的问题和错误!

准备知识      首先你安装好了,然后用一个测试文件(没有测试文件可以找后面教程中的图像显示的代码粘贴),可以正常的运行。

      然后还不要着急去学习怎么图像处理,因为还要知道一些常识。

     准备知识也是很重要

         模块         #include         namespace         argc argv         Mat

(1) 模块

          在这个文件下D:\ProgramFiles\opencv\build\include\opencv2会看到很多东西,这些都是需要的模块,里面有很多的要用的东西

          

       介绍这个就不说了

       因为是图像处理,先不用管那么多的模块,先去认识core imgproc  highgui这三个,其他的用到的时候再去看吧

       为 什么要看这个三个呢?

    一、Core 核心功能模块(核心所以必须要啊),主要包含opencv基本数据结构,动态数据结构,绘图函数,数组操作相关函数,听听这些名字就知道必须要用了

    二、Imgproc 这是图像处理模块,既然是处理图像,那也是没的说了

    三、Highgui GUI图形用户界面,简单的说,你要用窗口,用界面,什么输入输出了,那你就用他的模块了

 (2)#include

           正如你经常看到的,每个代码的前面都会有这养的include,包含才可用,学过c++的应该很懂

        #include         #include

   (3) namespace

          还有一个需要注意的是这样的话

        using namespace cv;        using namespace std;

        因为OpenCV中的C++类和函数都是定义在命名空间cv之内的,想要用,就的提前给人家打声招呼,所以要加上using namespacecv;这个方法比较好,只要一句话就可以,但是如果你加这一句,那么后面如果你要用类和函数的话,你就的这样写,很麻烦,std同理,std是一个类(输入输出标准),它包括了cin成员和cout成员,usingname space std ;以后才能使用它的成员。

    image=cv::imread(“lena.jpg”);    cv::nameWindow(“original image”);    cv::imshow(“original image”,image);

   (4) argc argv

    对于一个程序来说,都会有一个main()函数

    例如我们常用的一句话

     main(int argv char **argv)

         这里的int argc 为整型,用来统计程序运行时,发送给main函数的命令行参数;char **argv 是字符串数组用来存放指向字符串参数的指针数组,每一个元素指向一个参数

imread(argv[1],1),//意思是读取字符串名为argv[1]的图片,argv[1]指向在DOS命令行中执行程序名后的第一个字符串

(5)Mat

       自从版本2.0,OpenCV采用了新的数据结构,用Mat类结构取代了之前用extended C写的cvMat和lplImage(在学习代码的时候,会经常看到IplImage),更加好用啦,最大的好处就是更加方便的进行内存管理,对写更大的程序是很好的消息。

        如果光说Mat其中就可以是一篇很长的文章,因为用法太多,Mat是OpenCV里最基本的一个类,它用来表示图像,有部分组成,一个是矩阵头,一个是指向存储所有像素值的矩阵的指针,其中幅值与复制只是复制了信息头。下面给出超具体的例子,可以慢慢体会每一个Mat的应用,还有以前一篇写的复制问题可以参考http://blog.csdn.net/qq_20823641/article/details/51452939#include  #include  #include  #include  

using namespace std;using namespace cv;

int main(){

    //Mat() Constructor      Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));    cout 0"> 和 \beta 一般称作 增益 和 偏置 参数。我们往往用这两个参数来分别控制 对比度 和 亮度 。

你可以把 f(x) 看成源图像像素,把 g(x) 看成输出图像像素。这样一来,上面的式子就能写得更清楚些:

g(i,j) = \alpha \cdot f(i,j) + \beta

其中, i 和 j 表示像素位于 第i行 和 第j列 

让我看看这个应用,核心就是上面的公式

#include #include #include

using namespace std;using namespace cv;

double alpha; /**< 控制对比度 */int beta;  /**< 控制亮度 */

int main(int argc, char** argv){    /// 读入用户提供的图像    Mat image = imread("lena.jpg");    Mat new_image = Mat::zeros(image.size(), image.type());

    /// 初始化    //cout threshold ? max_value : 0 */

    CV_THRESH_BINARY_INV = 1,  /* value = value > threshold ? 0 : max_value */

    CV_THRESH_TRUNC = 2,  /* value = value > threshold ? threshold : value */

    CV_THRESH_TOZERO = 3,  /* value = value > threshold ? value : 0 */

    CV_THRESH_TOZERO_INV = 4,  /* value = value > threshold ? 0 : value */

    CV_THRESH_MASK = 7,

    CV_THRESH_OTSU = 8  /* use Otsu algorithm to choose the optimal threshold value; combine the flag with one of the above CV_THRESH_* values */

};

void cvAdaptiveThreshold(const CvArr * src, CvArr * dst, double max_value,    int adaptive_method = CV_ADAPTIVE_THRESH_MEAN_C,    int threshold_type = CV_THRESH_BINARY,    int block_size = 3, double param1 = 5);

src 输入图像.dst 输出图像.max_value使用 CV_THRESH_BINARY 和 CV_THRESH_BINARY_INV 的最大值.adaptive_method自适应阈值算法使用:CV_ADAPTIVE_THRESH_MEAN_C 或 CV_ADAPTIVE_THRESH_GAUSSIAN_C (见讨论).threshold_type取阈值类型:必须是下者之一

CV_THRESH_BINARY,

CV_THRESH_BINARY_INV

block_size用来计算阈值的象素邻域大小: 3, 5, 7, ...param1与方法有关的参数。对方法 CV_ADAPTIVE_THRESH_MEAN_C 和 CV_ADAPTIVE_THRESH_GAUSSIAN_C, 它是一个从均值或加权均值提取的常数(见讨论), 尽管它可以是负数。

二、代码应用

(1)

Threshold()

#include "opencv2/imgproc/imgproc.hpp"#include "opencv2/highgui/highgui.hpp"#include using namespace cv;using namespace std;Mat dst, gray, bluriamge;int thresh, thresh1;void on_thresh(int, void*){    threshold(bluriamge, dst, thresh, 255, thresh1);    imshow("threshold二值化灰图", dst);}

int main(){    Mat src;    src = imread("lena.jpg");    cvtColor(src, gray, CV_BGR2GRAY);    blur(gray, bluriamge, Size(3, 3));    imshow("threshold二值化灰图", src);    namedWindow("threshold二值化灰图");    createTrackbar("阈值:", "threshold二值化灰图", &thresh, 255, on_thresh);    createTrackbar("type:", "threshold二值化灰图", &thresh1, 4, on_thresh);    on_thresh(0, 0);    waitKey(0);    return 0;}

 

(2)adaptiveThreshold

#include "opencv2/imgproc/imgproc.hpp"#include "opencv2/highgui/highgui.hpp"#include using namespace cv;using namespace std;#include

int main(int argc, char** argv){    cv::Mat image = cv::imread("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);    if (image.empty())    {        std::cout = 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) = 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) imageData + src->widthStep * i;        for (int j = 0; j < width; j++)        {            histogram[*p++]++;        }    }    //normalize histogram          int size = height * width;    for (int i = 0; i < 256; i++)    {        histogram[i] = histogram[i] / size;    }

    //average pixel value          float avgValue = 0;    for (int i = 0; i < 256; i++)    {        avgValue += i * histogram[i];  //整幅图像的平均灰度        }

    int threshold;    float maxVariance = 0;    float w = 0, u = 0;    for (int i = 0; i < 256; i++)    {        w += histogram[i];  //假设当前灰度i为阈值, 0~i 灰度的像素(假设像素值在此范围的像素叫做前景像素) 所占整幅图像的比例            u += i * histogram[i];  // 灰度i 之前的像素(0~i)的平均灰度值: 前景像素的平均灰度值    

        float t = avgValue * w - u;        float variance = t * t / (w * (1 - w));        if (variance > maxVariance)        {            maxVariance = variance;            threshold = i;        }    }

    return threshold;}

int main(){    IplImage* img = cvLoadImage("lena.jpg", 0);    IplImage* bin = cvCreateImage(cvGetSize(img), 8, 1);

    int thresh = Otsu(img);    cvThreshold(img, bin, thresh, 255, CV_THRESH_BINARY);

    FillInternalContours(bin, 200);

    cvNamedWindow("img");    cvShowImage("img", img);

    cvNamedWindow("result");    cvShowImage("result", bin);

    cvWaitKey(-1);

    cvReleaseImage(&img);    cvReleaseImage(&bin);

    return 0;}

五、matlab辅助

I=imread('d:\22.png');SE=strel('rectangle',[3 3]);I2=imopen(I,SE);I3=imclose(I,SE);I4=im2bw(I);I5=bwmorph(I4,'thin',inf)figuresubplot(131),imshow(I2),title('开运算')subplot(132),imshow(I3),title('闭运算')subplot(133),imshow(I5),title('细化')

击中击不中

         在我们学习了膨胀腐蚀和基于膨胀腐蚀的变化之后,我比较喜欢的一个是击中击不中,因为喜欢所以就要单独列出来,心里总是觉得他可以有很多的用处,以后模版匹配,特征检测都会用,更深入的是,他会加深对膨胀腐蚀的理解,是一个很好的例子。

下面先看一个算法步骤和原理:

Hit-miss算法步骤:

击中击不中变换是形态学中用来检测特定形状所处位置的一个基本工具。它的原理就是使用腐蚀;如果要在一幅图像A上找到B形状的目标,我们要做的是:

首先,建立一个比B大的模板W;使用此模板对图像A进行腐蚀,得到图像假设为Process1;

其次,用B减去W,从而得到V模板(W-B);使用V模板对图像A的补集进行腐蚀,得到图像假设为Process2;

然后,Process1与Process2取交集;得到的结果就是B的位置。这里的位置可能不是B的中心位置,要视W-B时对齐的位置而异;

其实很简单,两次腐蚀,然后交集,结果就出来了。

Hit-miss原理:

基于腐蚀运算的一个特性:腐蚀的过程相当于对可以填入结构元素的位置作标记的过程。

    腐蚀中,虽然标记点取决于原点在结构元素中的相对位置,但输出图像的形状与此无关,改变原点的位置,只会导致输出结果发生平移。

    既然腐蚀的过程相当于对可以填入结构元素的位置作标记的过程,可以利用腐蚀来确定目标的位置。

    进行目标检测,既要检测到目标的内部,也要检测到外部,即在一次运算中可以同时捕获内外标记。

    由于以上两点,采用两个结构基元H、M,作为一个结构元素对B=(H,M),一个探测目标内部,一个探测目标外部。当且仅当H平移到某一点可填入X的内部,M平移到该点可填入X的外部时,该点才在击中击不中变换的输出中。

opencv应用

    Mat src;    src = imread("2312.png", 0);    Mat input_image = src;    Mat Kernel_S1 = imread("2311.png");    cvtColor(Kernel_S1, Kernel_S1, CV_RGB2GRAY);    int threhold = 180;    threshold(input_image, input_image, threhold, 255, CV_THRESH_BINARY);    threshold(Kernel_S1, Kernel_S1, threhold, 255, CV_THRESH_BINARY);    imshow("二值化图像", input_image);    Mat BigBlankimage = Mat::ones(input_image.size(), input_image.type());    input_image = BigBlankimage * 255 - input_image;    imshow("反置图像", input_image);    Mat Blankimage = Mat::ones(Kernel_S1.rows, Kernel_S1.cols, CV_8UC1);    Mat Kernel_S2 = Blankimage * 255 - Kernel_S1;    imshow("核1", Kernel_S1);    imshow("核2", Kernel_S2);    Mat hit_result, hit_result1, hit_result2;    erode(input_image, hit_result1, Kernel_S1, Point(-1, -1), 1, BORDER_DEFAULT, 0);    imshow("hit_result1", hit_result1);    erode(input_image, hit_result2, Kernel_S2, Point(-1, -1), 1, BORDER_DEFAULT, 0);    imshow("hit_result2", hit_result2);    hit_result = hit_result1 & hit_result2;    imshow("击中击不中", hit_result);

matlab应用之函数使用与自定义实现

只要定义好s1 s2 调用bwhitmiss

Ihm = bwhitmiss(I, S1, S2);% I为输入图像% S1、S2为结构元素根据冈萨雷斯的书的说明,如下图,然后用matlab编写代码,最后得到图像

clear all;I = zeros(120,180);I(11:80,16:75) = 1;I(56:105,86:135) = 1;I(26:55,141:170) = 1;figure,imshow(I); se = zeros(58,58);se(5:54,5:54) = 1;figure,imshow(se); %击中击不中变换Ie1 = imerode(I,se);figure,imshow(Ie1); Ic = 1 - I;figure,imshow(Ic);S2 = 1 - se;figure;imshow(S2);Ie2 = imerode(Ic,S2);figure,imshow(Ie2); Ihm = Ie1 & Ie2;figure,imshow(Ihm);这里我们看一下结果

Robert,prewitt,Sobel边缘检测

           图像的边缘检测,是根据灰度的突变或者说不连续来检测,对于其中的算子有一阶导数和二价导数,这里先说基础的三种方法---Robert,prewitt,Sobel边缘检测。

 一、梯度

          首先介绍下梯度,梯度并非是一个数值,梯度严格意义上是一个向量,这个向量指向当前位置变化最快的方向,可以这么理解,当你站在一个山上,你有360°的方向可以选择,哪个方向下降速度最快(最陡峭),便是梯度方向,梯度的长度,表示为向量的长度,表示最大的变化速率。

       梯度的数学表达:

   

     在二维中只取前两项,也就是由x方向的偏微分和y方向的偏微分组成。对于图像f中点(x,y)处的梯度,定义为:

与上面所述保持一致,图像梯度方向给出图像变化最快方向,当前点的梯度长度为:

次长度计算中有平方和开平方,所以将不再是现行操作。

为了简单计算,将上面求距离简化成:

二、RObert

        Roberts边缘算子是一个2x2的模板,采用的是对角方向相邻的两个像素之差。从图像处理的实际效果来看,边缘定位较准,对噪声敏感。适用于边缘明显且噪声较少的图像分割。Roberts边缘检测算子是一种利用局部差分算子寻找边缘的算子,Robert算子图像处理后结果边缘不是很平滑。经分析,由于Robert算子通常会在图像边缘附近的区域内产生较宽的响应,故采用上述算子检测的边缘图像常需做细化处理,边缘定位的精度不是很高。标准一阶差分不同,Robert采用对角线差分,边缘定位准,但是对噪声敏感。适用于边缘明显且噪声较少的图像分割。Roberts边缘检测算子是一种利用局部差分算子寻找边缘的算子,Robert算子图像处理后结果边缘不是很平滑。经分析,由于Robert算子通常会在图像边缘附近的区域内产生较宽的响应,故采用上述算子检测的边缘图像常需做细化处理,边缘定位的精度不是很高。

// roberts算子实现cv::Mat roberts(cv::Mat srcImage){    cv::Mat dstImage = srcImage.clone();    int nRows = dstImage.rows;    int nCols = dstImage.cols;    for (int i = 0; i < nRows - 1; i++)    {        for (int j = 0; j < nCols - 1; j++)        {            int t1 = (srcImage.at(i, j) -                srcImage.at(i + 1, j + 1)) *                (srcImage.at(i, j) -                    srcImage.at(i + 1, j + 1));            int t2 = (srcImage.at(i + 1, j) -                srcImage.at(i, j + 1)) *                (srcImage.at(i + 1, j) -                    srcImage.at(i, j + 1));            dstImage.at(i, j) = (uchar)sqrt(t1 + t2);

        }    }    return dstImage;}

三、prewitt

 Prewitt算子是一种一阶微分算子的边缘检测,利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用 。其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。

 对数字图像f(x,y),Prewitt算子的定义如下:

G(i)=|[f(i-1,j-1)+f(i-1,j)+f(i-1,j+1)]-[f(i+1,j-1)+f(i+1,j)+f(i+1,j+1)]|

G(j)=|[f(i-1,j+1)+f(i,j+1)+f(i+1,j+1)]-[f(i-1,j-1)+f(i,j-1)+f(i+1,j-1)]|

则 P(i,j)=max[G(i),G(j)]或 P(i,j)=G(i)+G(j)

经典Prewitt算子认为:凡灰度新值大于或等于阈值的像素点都是边缘点。即选择适当的阈值T,若P(i,j)≥T,则(i,j)为边缘点,P(i,j)为边缘图像。这种判定是欠合理的,会造成边缘点的误判,因为许多噪声点的灰度值也很大,而且对于幅值较小的边缘点,其边缘反而丢失了。

Prewitt算子对噪声有抑制作用,抑制噪声的原理是通过像素平均,但是像素平均相当于对图像的低通滤波,所以Prewitt算子对边缘的定位不如Roberts算子。

因为平均能减少或消除噪声,Prewitt梯度算子法就是先求平均,再求差分来求梯度。水平和垂直梯度模板分别为:

检测水平边沿 横向模板                 检测垂直平边沿 纵向模板:

该算子与Sobel算子类似,只是权值有所变化,但两者实现起来功能还是有差距的,据经验得知Sobel要比Prewitt更能准确检测图像边缘。

对噪声有抑制作用,抑制噪声的原理是通过像素平均,但是像素平均相当于对图像的低通滤波,所以Prewitt算子对边缘的定位不如Roberts算子。

// roberts算子实现Mat prewitt(Mat imageP){    cvtColor(imageP, imageP, CV_RGB2GRAY);    float prewittx[9] =    {        -1,0,1,        -1,0,1,        -1,0,1    };    float prewitty[9] =    {        1,1,1,        0,0,0,        -1,-1,-1    };    Mat px = Mat(3, 3, CV_32F, prewittx);    cout


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3